<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 3.2//EN">
<html>
<!-- #BeginTemplate "/Templates/dtvnormal.dwt" -->
<head>
<!-- #BeginEditable "doctitle" -->
<title>3:2 Pulldown Spec</title>
<!-- #EndEditable -->
<meta http-equiv="Content-Type" content=
"text/html; charset=utf-8">
<style type="text/css">
<!--
p {  font-family: Arial, Helvetica, sans-serif; font-size: 10pt}
.bottommenu {  font-family: Arial, Helvetica, sans-serif; font-size: 8pt; font-style: normal; text-decoration: none}
h3 {  font-family: Arial, Helvetica, sans-serif; font-size: 10pt; font-style: normal; font-weight: bold}
h1 {  font-family: Verdana, Arial, Helvetica, sans-serif; font-size: 18pt; font-weight: bold}
li {  font-family: Arial, Helvetica, sans-serif; font-size: 10pt}
h2 {  font-family: Arial, Helvetica, sans-serif; font-size: 14pt; font-weight: bold}
ol {  font-family: Arial, Helvetica, sans-serif; font-size: 10pt}
blockquote {  font-family: Arial, Helvetica, sans-serif; font-size: 10pt}
pre {  font-family: "Courier New", Courier, mono; font-size: 9pt}
ul {  font-family: Arial, Helvetica, sans-serif; font-size: 10pt}
-->
</style>
</head>
<body bgcolor="#FFFFFF">
<table width="100%" border="0" cellspacing="0" cellpadding="0">
<tr>
<td height="1" width="160"><img src="images/spacer.gif" width="1"
height="1"></td>
<td height="1" width="342"><img src="images/spacer.gif" width="1"
height="1"></td>
<td height="1" width="501"><img src="images/spacer.gif" width="1"
height="1"></td>
</tr>

<tr>
<td width="160"><img src="images/dscalerlogo.jpg" alt=
"DScaler Logo"></td>
<td colspan="2" align="right" valign="bottom"><font face=
"Verdana, Arial, Helvetica, sans-serif" size="5"><b>
<!-- #BeginEditable "Page%20Title" -->
3:2 Pulldown Alogrithm 
<!-- #EndEditable --></b></font></td>
</tr>

<tr bgcolor="#FFCC00" align="right">
<td colspan="3"><img src="images/yellowspacer.gif" width="100%"
height="2"></td>
</tr>

<tr>
<td colspan="3"> </td>
</tr>

<tr>
<td colspan="3"><!-- #BeginEditable "Text" -->
<h1>Mark Rejhon's 3:2 Pulldown Algorithm</h1>

<p>Here's a simplified pseudocode implementation of <a href=
"http://www.avsforum.com/ubb/Forum12/HTML/000071.html" target=
"_blank">my 3:2 pulldown detection algorithm</a>. You might even
easily be able to implement this into DScaler. Almost all of us
really don't care about 2:2 pulldown at all as I only see this
happening only 0.1 to 0.5% of the time on NTSC television.
Without further ado, here you go!</p>

<p><b>START PSEUDOCODE FOR AUTOMATIC 3:2 PULLDOWN
DETECTION</b></p>

<code>
<pre>
set MAX_MISMATCH = 12
set MAX_VERIFY_CYCLES = 2
set MISMATCH_COUNT = 0
set MOVIE_FIELD_CYCLE = 0
set MOVIE_VERIFY_CYCLE = 0
set MODE = VIDEO
set FRAMES_PER_SECOND = 60
LOOP
    IF new field has now arrived on TV card
        //
        // Automatic detection code starts here
        // Compares a pair of even fields or a pair of odd fields.
        //
        GRAB new current field (add to queue of fields)
        COMPARE two fields: current field and two fields ago.
        IF fields DO NOT match THEN
            IF MISMATCH_COUNT does not exceed MAX_MISMATCH
                increment MISMATCH_COUNT by 1
                //
                // MISMATCH_COUNT is the number of consecutive pairs
                // of fields that do not match.
            ELSE
                set MODE = VIDEO
                set MOVIE_VERIFY_CYCLE = 0
                set MOVIE_FIELD_CYCLE = 0
                //
                // There has been no duplicate fields lately.
                // It's probably video source.
                //
                // MAX_MISMATCH should be a reasonably high value so
                // that we do not have an oversensitive hair-trigger
                // in switching to video source everytime there is
                // video noise or a single spurious field added/dropped
                // during a movie causing mis-synchronization problems. 
            ENDIF
        ELSE IF fields match THEN
            // It's either a stationary image OR a duplicate field in a movie
            IF MISMATCH_COUNT is 4
                //
                // 3:2 pulldown is a cycle of 5 fields where there is only
                // one duplicate field pair, and 4 mismatching pairs.
                // We need to continue detection for at least 2 cycles
                // to be very certain that it is actually 3:2 pulldown
                // This would mean a latency of 10 fields.
                //
                IF MOVIE_VERIFY_CYCLE equals or exceeds MAX_VERIFY_CYCLES
                    //
                    // This executes regardless whether we've just entered or
                    // if we're *already* in 3:2 pulldown. Either way, we are
                    // currently now (re)synchronized to 3:2 pulldown and that
                    // we've now detected the duplicate field.
                    //
                    set MODE = MOVIE
                    set MOVIE_FIELD_CYCLE = 0
                ELSE
                    increment MOVIE_VERIFY_CYCLE by 1
                ENDIF
            ELSE MISMATCH_COUNT not equal to 4
                //
                // If execution point hits here, it is probably just
                // stationary video. That would produce lots of duplicate
                // field pairs. We can't determine whether it's video or
                // movie source. Therefore, we want to keep doing the
                // current algorithm from a prior detection. Therefore,
                // don't modify any variables EXCEPT the MISMATCH_COUNT
                // variable which will be reset to 0 below.
                // Also, occasionally we'll hit here during synchronization
                // problems during a movie, such as a spurious field added
                // or dropped. Reset MISMATCH_COUNT to 0 below and let
                // detection cycle happen again in order to re-synchronize.
                // Keep the MODE variable the way it currently is.
            ENDIF
            set MISMATCH_COUNT = 0
         ENDIF
         // Now, process the images depending on present mode
         IF MODE is MOVIE
            // You could replace below with movie source deinterlacer plugin
            //
            set FRAMES_PER_SECOND = 24
            BEGIN movie-source deinterlacer
                IF MOVIE_FIELD_CYCLE equals 2 or MOVIE_FIELD_CYCLE equals 4
                    Weave current field with previous field
                    Add resulting field to OUTPUT queue
                ELSE
                     // Do nothing here.
                     // Do not add frame to OUTPUT queue.
                     // During MOVIE_FIELD_CYCLE 0 and 3, the current and
                     // previous fields come from different movie frames.
                     // During MOVIE_FIELD_CYCLE 1, the current and previous
                     // fields identical movie frame as MOVIE_FIELD_CYCLE 2.
                     // Therefore, we skip these cycle values.
                ENDIF
                    // Looping counter that goes from 0, 1, 2, 3, 4 and back to 0
                    increment MOVIE_FIELD_CYCLE by 1
                IF MOVIE_FIELD_CYCLE equals 5 THEN
                    set MOVIE_FIELD_CYCLE = 0
                ENDIF
            END
        ELSEIF MODE is VIDEO
            // You could replace below with video source deinterlacer plugin
            //
            set FRAMES_PER_SECOND = 60
            BEGIN video-source deinterlacer
                Apply video source deinterlace algorithm
                Add resulting field to OUTPUT queue
            END
        ENDIF
    ENDIF
    // You can put video output code here for the OUTPUT queue of frames.
    // You can optionally synchronize to the output refresh this way.
ENDLOOP
</pre>
</code>

<hr>
</blockquote>

<p>.</p>

<p><b>EXAMPLE SEQUENCE OF FRAMES FROM 3:2 PULLDOWN</b></p>

<p>Field 00: Movie frame 1<br>
Field 01: Movie frame 1<br>
Field 02: Movie frame 1<br>
Field 03: Movie frame 2<br>
Field 04: Movie frame 2<br>
Field 05: Movie frame 3<br>
Field 06: Movie frame 3<br>
Field 07: Movie frame 3<br>
Field 08: Movie frame 4<br>
Field 09: Movie frame 4<br>
Field 10: Movie frame 5<br>
Field 11: Movie frame 5<br>
Field 12: Movie frame 5<br>
Field 13: Movie frame 6<br>
Field 14: Movie frame 6</p>

<p>Field 02 and 00 is MATCH --- MOVIE_FIELD_CYCLE = 0<br>
Field 03 and 01 is mismatch - MOVIE_FIELD_CYCLE = 1<br>
Field 04 and 02 is mismatch - MOVIE_FIELD_CYCLE = 2<br>
Field 05 and 03 is mismatch - MOVIE_FIELD_CYCLE = 3<br>
Field 06 and 04 is mismatch - MOVIE_FIELD_CYCLE = 4<br>
Field 07 and 05 is MATCH --- MOVIE_FIELD_CYCLE = 0<br>
Field 08 and 06 is mismatch - MOVIE_FIELD_CYCLE = 1<br>
Field 09 and 07 is mismatch - MOVIE_FIELD_CYCLE = 2<br>
Field 10 and 08 is mismatch - MOVIE_FIELD_CYCLE = 3<br>
Field 11 and 09 is mismatch - MOVIE_FIELD_CYCLE = 4<br>
Field 12 and 10 is MATCH --- MOVIE_FIELD_CYCLE = 0<br>
Field 13 and 11 is mismatch - MOVIE_FIELD_CYCLE = 1<br>
Field 14 and 12 is mismatch - MOVIE_FIELD_CYCLE = 2</p>

<p>.</p>

<p><b>OPTIONAL: BETTER SYNCHRONIZATION TO MONITOR REFRESH</b></p>

<p>It is desirable to make the output refresh independent of the
input refresh for better synchronization. During 3/2 pulldown
material, it is very desirable to synchronize to the monitor
refresh instead of the input TV signal refresh. During watching
in normal TV viewers, 3/2 pulldown is doubled to 6/4 pulldown
when the refresh rate is doubled to 120 Hz. It would be very nice
to have something closer to 5/5 pulldown when watching movie
material instead of 6/4 pulldown.</p>

<p>In the pseudocode above in the last section,<br>
There are 24 frames per second in OUTPUT queue in MOVIE mode<br>
There are 60 frames per second in OUTPUT queue in VIDEO mode</p>

<p>You can synchronize the OUTPUT queue of fields to the monitor
refresh, based on the value in the FRAMES_PER_SECOND variable. By
keeping a small output buffer of frames (of about 2 or so frames)
you can keep the pipeline of output frames flowing smoothly and
synchronized much better to the computer monitor.</p>

<p>Also, since the computer clock is accurate, this can help
"stabilize" unstable NTSC refresh such as those from a VCR which
can sometimes have slightly-varying refresh rates (fluctuating
randomly as low as 59Hz all the way up to 61Hz although usually
by smaller increments). This can interact badly with a consistent
computer refresh. Regular TV sets have a very slight amount of
"give" that allows it to compensate for very minor fluctuations
in refresh rate, but computer monitors won't compensate.</p>

<p>It can also smooth out jerkiness problems caused by
fluctuating CPU usage on a frame-by-frame basis, since
deinterlacing one pair of frames might take longer than
deinterlacing a different pair of frames (using certain video
source deinterlacing algorithms).</p>

<p>There is also the problem of compensating for drift when the
output framerate is decoupled from the refresh of the input
signal. There are two good possible ways of getting around this
drift problem:</p>

<p>(1) AVOID displaying the output frame if there is less than 2
frames in the OUTPUT queue, and ACCELERATE displaying of the next
frame if the number of frames in the OUTPUT queue exceeds a
predetermined value such as 4. That way, you always keep 2, 3 or
4 frames buffered at all times for maximum fluidity. (You could
experiment, and set the minimum to 1 and maximum to 3, to find
out which produces the best fluidity at the minimum possible
buffering. Too much buffering will cause audio to be noticeably
lagged behind the video)</p>

<p>(2) Synchronize to a clock that's generated from the TV tuner
card, instead of using the computer's clock. For example,
increment an integer clock counter by 1 everytime a field arrives
from the TV card. This clock would be incremented about 59.94
times per second for most NTSC signals. Keep an eye on the
computer's clock everytime you increment the integer clock by 60
- to calculate the exact amount of time it takes for 60 fields to
hit the TV tuner card. Do this continously every second. Divide
this value by 24 to compute the amount of time there should be
between frames, during MOVIE mode, during the next second. (If
you prefer, you could use 5 second cycles instead of 1 second
cycles, or keep a "rolling average" every field based on the
previous X number of fields).</p>

<p>.</p>

<p><b>APPENDIX: PSEUDOCODE WITHOUT COMMENTS</b></p>

<p>For determining the amount of "code bulk", this is how small
the pseudocode is, when I remove my comments from the above
pseudocode. Look at how small the pseudocode is - since you
already have the routines written in DScaler, look at how simple
it is to implement automatic 3:2 pulldown deinterlacing!</p>

<code>
<pre>
set MAX_MISMATCH = 12
set MAX_VERIFY_CYCLES = 2
set MISMATCH_COUNT = 0
set MOVIE_FIELD_CYCLE = 0
set MOVIE_VERIFY_CYCLE = 0
set MODE = VIDEO
set FRAMES_PER_SECOND = 60
LOOP
   IF new field has now arrived on TV card
     GRAB new current field (add to queue of fields)
     COMPARE two fields: current field and two fields ago.
     IF fields DO NOT match THEN
         IF MISMATCH_COUNT does not exceed MAX_MISMATCH
             increment MISMATCH_COUNT by 1
         ELSE
             set MODE = VIDEO
             set MOVIE_VERIFY_CYCLE = 0
             set MOVIE_FIELD_CYCLE = 0
         ENDIF
     ELSE IF fields match THEN
         IF MISMATCH_COUNT is 4
             IF MOVIE_VERIFY_CYCLE equals or exceeds MAX_VERIFY_CYCLES
                 set MODE = MOVIE
                 set MOVIE_FIELD_CYCLE = 0
             ELSE
                 increment MOVIE_VERIFY_CYCLE by 1
             ENDIF
         ELSE MISMATCH_COUNT not equal to 4
         ENDIF
         set MISMATCH_COUNT = 0
     ENDIF
    
     IF MODE is MOVIE
         set FRAMES_PER_SECOND = 24
         BEGIN movie-source deinterlacer
             IF MOVIE_FIELD_CYCLE equals 2 or MOVIE_FIELD_CYCLE equals 4
                  Weave current field with previous field
                  Add resulting field to OUTPUT queue
             ENDIF
             increment MOVIE_FIELD_CYCLE by 1
             IF MOVIE_FIELD_CYCLE equals 5 THEN
                 set MOVIE_FIELD_CYCLE = 0
             ENDIF
         END
     ELSEIF MODE is VIDEO
         set FRAMES_PER_SECOND = 60
         BEGIN video-source deinterlacer
             Apply video source deinterlace algorithm
             Add resulting field to OUTPUT queue
         END
     ENDIF
   ENDIF
ENDLOOP
</pre>
</code>

<p><font size="2" face="Verdana, Arial">(c) Mark Rejhon
2000</font></p>

<!-- #EndEditable -->
</td>
</tr>

<tr>
<td colspan="3"> </td>
</tr>

<tr bgcolor="#FFCC00">
<td colspan="3">
<div align="center"><img src="images/yellowspacer.gif" width=
"100%" height="2"></div>
</td>
</tr>
</table>

<p> </p>

<!-- #EndTemplate -->
</body>
</html>

